NOIP2017 奶酪

思路如下

首先先写一个函数判断两个洞是否相连,即两洞之间距离是否小于等于球直径(注意是直径)

1
2
3
4
5
6
7
8
9
10
struct dong {//为了程序简洁美观,定义一个结构体表示洞的坐标
double x,y,z;
};
dong p[1001];
bool pd(dong a,dong b) {
long long d;//d为两洞之间距离的平方
d=(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
if(d<=2*2*r*r) return true;//鉴于sqrt函数精度问题,我们就用平方判断(直径2r的平方)
else return false;
}

第二个难点在于如何判断是否可以穿过奶酪,对此我们可以模拟老鼠钻洞(run函数)见代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void run(int x) {
if(jud==1) return;//用jud记录是否穿过了奶酪
if(p[x].z+r>=h) {
jud=1;
return;
}
for(int i=1; i<=n; i++) {
if(m[i]==1) continue;//用m数组保存是否走过此洞(1为走过,0为没走过)
if(pd(p[x],p[i])) {//如果能走
m[i]=1;//标记为走过
run(i);//从这个洞往别的洞走
}
}
}

思考一下,对于这个函数我们要从最下面的一个洞开始走,凡是走过的洞都不会再走,但如果前方不能再走了还会返回这个洞寻找下一个能连接到此洞的洞,这样的话就可以将每一个能和底边连接到的洞全部判断一遍,只要其中有洞连接到顶端,就用jud记录为1。因此等会在主函数中我们用循环把能连接到底边的所有洞找到均调用此函数即可。

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<cstdio>
using namespace std;
unsigned long long r,n,h,jud;//定义在最前面,很多函数都要用到
unsigned long long m[1001];//上文提到过的用来记录该洞是否走过的数组
struct dong {
double x,y,z;
};
dong p[1001];
bool pd(dong a,dong b) {
long long d;
d=(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
if(d<=4*r*r) return true;
else return false;
}
void run(int x) {
if(jud==1) return;
if(p[x].z+r>=h) {
jud=1;
return;
}
for(int i=1; i<=n; i++) {
if(m[i]==1) continue;
if(pd(p[x],p[i])) {
m[i]=1;
run(i);
}
}
}
int main() {
int T;
scanf("%d",&T);
for(int j=1; j<=T; j++) {
scanf("%d%d%d",&n,&h,&r);
jud=0;
for(int i=1; i<=n; i++) m[i]=0;
for(int i=1; i<=n; i++)
scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);//输入坐标
for(int i=1; i<=n; i++)
if(p[i].z<=r) {//判断是不是底边洞
m[i]=1;
run(i);
}
if(jud==1) printf("Yes\n");
else printf("No\n");
}
return 0;
}